<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .draggable {
            cursor: move;
            user-select: none;
            color: #fff;
            background-color: #000;
            text-align: center;
            line-height: 50px;
            border-width: 5px;
            border-color: aqua;
            width: 100px;
            height: 50px;
            margin-top: 10px;
        }
    </style>
</head>

<body>
    <div id="list">
        <div class="draggable">5</div>
        <div class="draggable">3</div>
        <div class="draggable">2</div>
        <div class="draggable">4</div>
        <div class="draggable">1</div>
    </div>
    <script>
        // The current dragging item
        let draggingEle;

        // The current position of mouse relative to the dragging element
        let x = 0;
        let y = 0;


        const isAbove = function (nodeA, nodeB) {
            // Get the bounding rectangle of nodes
            const rectA = nodeA.getBoundingClientRect();
            const rectB = nodeB.getBoundingClientRect();
    
            return rectA.top + rectA.height / 2 < rectB.top + rectB.height / 2;
        };
    
        const mouseDownHandler = function (e) {
            draggingEle = e.target;
    
            // Calculate the mouse position
            const rect = draggingEle.getBoundingClientRect();
            x = e.pageX - rect.left;
            y = e.pageY - rect.top;
    
            // Attach the listeners to `document`
            document.addEventListener('mousemove', mouseMoveHandler);
            document.addEventListener('mouseup', mouseUpHandler);
        };
    
        const swap = function (nodeA, nodeB) {
            const parentA = nodeA.parentNode;
            const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
    
            // Move `nodeA` to before the `nodeB`
            nodeB.parentNode.insertBefore(nodeA, nodeB);
    
            // Move `nodeB` to before the sibling of `nodeA`
            parentA.insertBefore(nodeB, siblingA);
        };
    
        let placeholder;
        let isDraggingStarted = false;
    
        const mouseMoveHandler = function (e) {
            const draggingRect = draggingEle.getBoundingClientRect();
    
            if (!isDraggingStarted) {
                // Update the flag
                isDraggingStarted = true;
    
                // Let the placeholder take the height of dragging element
                // So the next element won't move up
                placeholder = document.createElement('div');
                placeholder.classList.add('placeholder');
                draggingEle.parentNode.insertBefore(
                    placeholder,
                    draggingEle.nextSibling
                );
    
                // Set the placeholder's height
                placeholder.style.height = `${draggingRect.height}px`;
            }
            // Set position for dragging element
            draggingEle.style.position = 'absolute';
            draggingEle.style.top = `${e.pageY - y}px`;
            draggingEle.style.left = `${e.pageX - x}px`;
            const prevEle = draggingEle.previousElementSibling;
            const nextEle = placeholder.nextElementSibling;
            // User moves item to the top
            if (prevEle && isAbove(draggingEle, prevEle)) {
                // The current order    -> The new order
                // prevEle              -> placeholder
                // draggingEle          -> draggingEle
                // placeholder          -> prevEle
                swap(placeholder, draggingEle);
                swap(placeholder, prevEle);
                return;
            }
            // User moves the dragging element to the bottom
            if (nextEle && isAbove(nextEle, draggingEle)) {
                // The current order    -> The new order
                // draggingEle          -> nextEle
                // placeholder          -> placeholder
                // nextEle              -> draggingEle
                swap(nextEle, placeholder);
                swap(nextEle, draggingEle);
            }
        };
    
        const mouseUpHandler = function () {
            // Remove the placeholder
            placeholder && placeholder.parentNode.removeChild(placeholder);
            // Reset the flag
            isDraggingStarted = false;
            // Remove the position styles
            draggingEle.style.removeProperty('top');
            draggingEle.style.removeProperty('left');
            draggingEle.style.removeProperty('position');
    
            x = null;
            y = null;
            draggingEle = null;
    
            // Remove the handlers of `mousemove` and `mouseup`
            document.removeEventListener('mousemove', mouseMoveHandler);
            document.removeEventListener('mouseup', mouseUpHandler);
        };
        // Query the list element
        const list = document.getElementById('list');
    
        // Query all items
        [].slice.call(list.querySelectorAll('.draggable')).forEach(function (item) {
            item.addEventListener('mousedown', mouseDownHandler);
        });
    </script>
</body>

</html>